package net.jueb.sniff4j.extr.base;
import java.util.HashMap;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
import net.jueb.sniff4j.extr.auth.AddressCollection;
import net.jueb.sniff4j.extr.auth.AddressRedirectCollection;
import net.jueb.sniff4j.extr.auth.User;
import net.jueb.sniff4j.extr.auth.Users;
import net.jueb.sniff4j.extr.filter.DefaultObjectFilter;

public abstract class AbstractRuntime {
	public static final AttributeKey<AbstractRuntime> bindKey=AttributeKey.valueOf("AbstractRuntime");
	
	/**
	 * 因为不允许子类修改，但是又不能设置为final，所以私有
	 */
	private  final BootConfig bootConfig=new BootConfig("0.0.0.0",1080, true);
	/**
	 * 不允许连接到代理服务器的客户端地址集合
	 */
	private final AddressCollection refuseClients=new AddressCollection();
	/**
	 * 不接受的代理目标，全局服务器使用，优先级高于用户的地址集合
	 */
	private final AddressCollection refuseProxyTargets=new AddressCollection();
	
	/**
	 * 地址重定向集合，如果允许访问的目标地址存在，则根据重定向规则，连接新的目标地址
	 * 此集合优先级高于用户集合
	 */
	private final AddressRedirectCollection targetRedirects=new AddressRedirectCollection();
	
	/**
	 * 允许代理的用户
	 */
	private final Users allowUsers=new Users();
	/**
	 * 服务端使用的全局上下文
	 */
	private final ServerContext serverConext=new ServerContext();
	/**
	 * 用户上下文，用于客户端使用
	 */
	private final HashMap<Channel,ChannelContext> channelContexts=new HashMap<Channel,ChannelContext>();
	
	/**
	 * 用户上下文
	 * 以userName为key
	 */
	private final HashMap<String,UserContext> usersConexts=new HashMap<String,UserContext>();
	
	{
		initBootConfig(bootConfig);
		initRefuseClients(refuseClients);
		initRefuseProxyTargets(refuseProxyTargets);
		initAllowUsers(allowUsers);
		initServerContext(serverConext);
		creatUsersContexts();
		initUsersContexts(usersConexts);
	}
	/**
	 * 创建现有用户的上下文
	 */
	private void creatUsersContexts()
	{
		for(String name:this.allowUsers.keySet())
		{
			User user=this.allowUsers.getUser(name);
			initUserContext(user);
		}
	}
	
	/**
	 * 获取服务端启动配置
	 * @return
	 */
	public final BootConfig getBootConfig()
	{
		return this.bootConfig;
	}

	/**
	 * 获取runtime在conext中存储的key值
	 * @return
	 */
	public final AttributeKey<AbstractRuntime> getBinAttributeKey()
	{
		return bindKey;
	}
	/**
	 * 绑定服务端
	 * @param sb
	 * @return
	 */
	public final void  bindServer(ServerBootstrap sb)
	{
		sb.attr(getBinAttributeKey(), this);
	}
	/**
	 * 绑定到通道
	 * @param sb
	 * @return
	 */
	public final void  bindChannel(Channel channel)
	{
		channel.attr(bindKey).set(this);
	}
	
	/**
	 * 获取允许登陆的用户集合
	 * @return
	 */
	public final Users getAllowUsers()
	{
		return this.allowUsers;
	}
	/**
	 * 获取服务端使用的ctxmap
	 * @return
	 */
	public final ServerContext getServerConext()
	{ 
		return this.serverConext;
	}
	
	/**
	 * 此方法在认证成功后被调用
	 * 将一个管道上下文以channel登记
	 * 如果这个通道已经登记过了，就不登记
	 * 并且登记离线用户上下文
	 * @param channel
	 * @param userConext
	 */
	public  final void initChannelContext(Channel channel,ChannelContext channelContext)
	{
		if(!this.channelContexts.containsKey(channel))
		{//如果这个通道没有对应的usercontext，则绑定
			this.channelContexts.put(channel, channelContext);
		}
	}
	/**
	 * 获取以当前channel登记的用户上下文
	 * @param channel
	 * @return
	 */
	public final ChannelContext getChannelContext(Channel channel)
	{ 
		return this.channelContexts.get(channel);
	}
	/**
	 * 如果可能，根据当前通道获取关联的通道上下文，并取出上下文中的绑定user名字，然后根据名字获取服务端配置的User对象
	 * @param channel
	 * @return
	 */
	public final User getCurrentUser(Channel channel)
	{
		User user=null;
		ChannelContext cc=this.channelContexts.get(channel);
		if(cc!=null)
		{
			user=this.getAllowUsers().getUser(cc.getUserName());
		}
		return user;
	}
	
	/**
	 * 获取当前管道所关联的用户的离线上下文
	 * @param channel
	 * @return
	 */
	public final UserContext getCurrentUserContext(Channel channel)
	{
		UserContext ctx=null;
		User user=null;
		ChannelContext cc=this.channelContexts.get(channel);
		if(cc!=null)
		{
			user=this.getAllowUsers().getUser(cc.getUserName());
			ctx=this.usersConexts.get(user.getName());
		}
		return ctx;
	}
	
	/**
	 * 以user对象重新初始化一个用户离线上下文
	 * @param user
	 */
	public final UserContext initUserContext(User user)
	{
		UserContext userOfflineContext=new UserContext(user);
		this.usersConexts.put(user.getName(),userOfflineContext);
		return userOfflineContext;
	}
	/**
	 * 根据用户的用户名为key获取用户的可离线上下文
	 * @param user
	 * @return
	 */
	public final UserContext getUserContext(User user)
	{
		return this.usersConexts.get(user.getName());
	}
	
	public final UserContext getUserContext(String userName)
	{
		return this.usersConexts.get(userName);
	}
	
	/**
	 * 当通道被关闭的时候，删除登记的上下文，因为用户上下文是临时的
	 * @param channel
	 */
	public final void removeChannelContext(Channel channel)
	{
		this.channelContexts.remove(channel);
	}
	/**
	 * 获取拒绝连接代理服务器的地址集合
	 * @return
	 */
	public final AddressCollection getRefuseClients()
	{
		return this.refuseClients;
	}
	/**
	 * 获取代理服务器拒绝代理的目标地址
	 * @return
	 */
	public final AddressCollection getRefuseProxyTargets()
	{
		return this.refuseProxyTargets;
	}
	
	public final AddressRedirectCollection getTargetRedirects() {
		return targetRedirects;
	}

	/**
	 * 初始化服务端启动配置对象
	 * 默认已经为：new BootConfig("0.0.0.0", "1080", true)
	 * 如果不配置，则保持默认
	 * @param bootConfig
	 */
	protected abstract void initBootConfig(BootConfig bootConfig);
	/**
	 * 初始化拒绝连接代理服务器的ip集合
	 * 如果集合存在一个符合的地址，则客户端会被拒绝连接。
	 */
	protected abstract void initRefuseClients(AddressCollection refuseClients);
	/**
	 * 初始化代理服务器不支持的代理目标地址，
	 * 如果客户端请求的目标地址存在于这个集合，则向客户端返回拒绝代理
	 * @param refuseProxyTargets
	 */
	protected abstract void initRefuseProxyTargets(AddressCollection refuseProxyTargets);
	/**
	 * 设置允许代理的用户到集合<br>
	 * 默认初始化,在对象创建完成之前，构造器之前被执行<br>
	 * 此方法留给子类使用，不对外<br>
	 * allowUsers里面的每个用户存在一个userContext<br>
	 * 在runtime初始化的时候被创建<br>
	 */
	protected abstract void initAllowUsers(Users allowUsers);

	/**
	 * 默认初始化,在对象创建完成之前，构造器之前被执行
	 * 此方法留给子类使用，不对外
	 */
	protected abstract void initServerContext(ServerContext serverConext);

	/**
	 * 默认所有用户的上下文没有任何数据，此方法可以进行上下文数据默认配置<br>
	 * usersContexts.get("test").set("status","login");<br>
	 * @param usersContexts
	 */
	protected abstract void initUsersContexts(HashMap<String,UserContext> usersContexts);
	
	/**
	 * 获取代理成功后的2个通道的初始化类<br>
	 * 注意：此返回对象的init方法每次初始化的管道不能被重复，即初始化的对象不能在内部保留<br>
	 * @param proxyClientChannel
	 * @param proxyTargeChannel
	 * @return
	 */
	public abstract AbstractChannelCodecInit getChannelCodecInit();
	/**
	 * 服务端级别的默认重建过滤器，默认为null，不过滤<br>
	 * 如果user的过滤器为null，则默认使用此方法返回值<br>
	 * fromChannel是proxytargetChannel<br>
	 * toChannel是proxyClientChannel<br>
	 * 因为数据是从target返回到客户端<br>
	 * 指对返回数据response的过滤器<br>
	 * 如果返回null，则对象不会被过滤和重建
	 * @return
	 */
	public  DefaultObjectFilter getDefaultToClientFilter(Channel fromChannel, Channel toChannel)
	{
		return null;
	}
	/**
	 * 服务端级别的默认重建过滤器，默认为null，不过滤<br>
	 * 如果user的过滤器为null，则默认使用此方法返回值<br>
	 * fromChannel是proxyClientChannel<br>
	 * toChannel是proxytargetChannel<br>
	 * 因为数据是从client发送到目标端<br>
	 * 指对请求数据request的过滤器<br>
	 * 如果返回null，则对象不会被过滤和重建
	 * @return
	 */
	public  DefaultObjectFilter getDefaultToTargetFilter(Channel fromChannel, Channel toChannel)
	{
		return null;
	}
	
	/**
	 * 根据通道获取绑定的runtime
	 * 因为一个通道只能有一个runtime，所以runtime名字可以是唯一的
	 * 如果拿不到，则在父管道去拿，即server启动时绑定的runtime
	 * @param channel
	 * @return
	 */
	public final static AbstractRuntime getRuntime(Channel channel)
	{
		AbstractRuntime rt=null;
		try {
			rt=channel.attr(bindKey).get();
			if(rt==null)
			{
				rt=channel.parent().attr(bindKey).get();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return rt;
	}
}
